package org.uned.agonzalo16.bitacora.service.security;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.uned.agonzalo16.bitacora.dao.UserDao;
import org.uned.agonzalo16.bitacora.domain.User;
import org.uned.agonzalo16.bitacora.domain.UserType;

@Service("AuthenticationProvider")
public class AuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements UserDetailsService {

	@Autowired
	private UserDao userDao;

	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	@Override
	protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
		// Overridden null method to comply with provider
	}

	@Override
	protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
		String password = (String) authentication.getCredentials();
		if (!StringUtils.hasText(password)) {
			throw new BadCredentialsException("Please enter password");
		}
		return checkUserDetails(username, password, true);
	}

	private void insertAdmin() {
		User user = new User();
		user.setActive(true);
		user.setCreationDate(new Date());
		user.setEmail("admin@test.com");
		user.setLocalization("Spain");
		user.setPassword("password");
		user.setType(UserType.ADMIN.getType());
		user.setUsername("admin");
		user.setWebSite("www.site.com");
		userDao.merge(user);
	}

	private UserDetails checkUserDetails(String username, String password, boolean authenticate) {

		if (userDao.findAll().isEmpty()) {
			insertAdmin();
		}

		User user = userDao.findByUsername(username);
		if (user == null) {
			throw new BadCredentialsException("Invalid user: " + username);
		}

		if (!user.isActive()) {
			throw new BadCredentialsException("User desactivated: " + username);
		}

		if (authenticate) {
			String expectedPassword = user.getPassword();
			if (!StringUtils.hasText(expectedPassword)) {
				throw new BadCredentialsException("No password for " + username + " set in database, contact administrator");
			}

			String encryptedPassword = DigestUtils.md5DigestAsHex(password.getBytes());
			if (!expectedPassword.equalsIgnoreCase(encryptedPassword)) {
				throw new BadCredentialsException("Invalid Password");
			}
		}

		return new AuthenticatedUser(user);
	}

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
		return checkUserDetails(username, "", false);
	}

	public AuthenticatedUser getCurrentUserDetails() {
		AuthenticatedUser details = null;
		SecurityContext securityContext = SecurityContextHolder.getContext();
		Authentication authentication = securityContext.getAuthentication();
		if (authentication != null && authentication.getPrincipal() instanceof AuthenticatedUser) {
			details = (AuthenticatedUser) authentication.getPrincipal();
		}
		return details;
	}
}
